package moe.launch;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class Console implements ActionListener, MouseListener {
	private final static String[] classRules = new String[] { "moe\\..+" };

	private Tray tray;
	private Object laucher;
	private Thread serverThread;

	private Runnable serverTask = new Runnable() {
		public void run() {
			if (laucher == null) {
				laucher = createLaucher();
			}
			Thread.currentThread().setContextClassLoader(laucher.getClass().getClassLoader());
			invokeLaucher("startup");
		}
	};
	private Runnable commandTask = new Runnable() {
		public void run() {
			System.out
					.println("Command: \n\t shutdown - shutdown this server. \n\t restart - restart this server.");
			BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
			while (true) {
				String command = null;
				try {
					command = br.readLine();
				} catch (IOException e) {
					continue;
				}
				if (command.equalsIgnoreCase("shutdown")) {
					System.out.println("Shutdowning server.");
					shutdown();
					break;
				} else if (command.equalsIgnoreCase("restart")) {
					System.out.println("Restarting server.");
					restart();
				}
			}
		}
	};

	private Object createLaucher() {
		ClassLoader classLoader = RuleClassLoader.createClassLoader(classRules);
		Object obj = null;
		try {
			Class<?> clazz = classLoader.loadClass("moe.launch.Launcher");
			obj = clazz.newInstance();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return obj;
	}

	private void invokeLaucher(String name) {
		try {
			Method method = laucher.getClass().getDeclaredMethod(name);
			method.setAccessible(true);
			method.invoke(laucher);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private void startup(boolean createTray) {
		if (createTray) {
			createTray();
		}
		Thread commandThread = new Thread(commandTask);
		commandThread.setName("Command Thread");
		commandThread.setDaemon(true);
		commandThread.start();

		serverThread = new Thread(serverTask);
		serverThread.setName("Server Thread");
		serverThread.setDaemon(true);
		serverThread.start();

		synchronized (this) {
			try {
				wait();
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
		}
	}

	private void shutdown() {
		if (tray != null) {
			tray.destroy();
		}
		invokeLaucher("shutdown");
		serverThread.interrupt();
		laucher = null;
		synchronized (this) {
			notify();
		}
	}

	private void restart() {
		invokeLaucher("shutdown");
		laucher = null;
		if (serverThread.isAlive()) {
			serverThread.interrupt();
		}
		serverThread = new Thread(serverTask);
		serverThread.setName("ServerThread");
		serverThread.setDaemon(true);
		serverThread.start();
	}

	private void createTray() {
		tray = new Tray(this);
		tray.createTray();
	}

	public void actionPerformed(ActionEvent e) {
		String command = e.getActionCommand();
		if ("exit".equals(command)) {
			shutdown();
		} else if ("browse".equals(command)) {
			// for some reason, IKVM ignores setActionCommand
			Browser.open();
		} else if ("restart".equals(command)) {
			restart();
		}
	}

	public void mouseClicked(MouseEvent e) {
		if (e.getButton() == MouseEvent.BUTTON1) {
			Browser.open();
		}
	}

	public void mouseEntered(MouseEvent e) {
		// nothing to do
	}

	public void mouseExited(MouseEvent e) {
		// nothing to do
	}

	public void mousePressed(MouseEvent e) {
		// nothing to do
	}

	public void mouseReleased(MouseEvent e) {
		// nothing to do
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Set<String> params = new HashSet<String>();
		params.addAll(Arrays.asList(args));
		new Console().startup(params.contains("tray"));
	}
}
